package Q7_10_Minesweeper;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import Q7_10_Minesweeper.Game.GameState;
public class Board {
private int nRows;
private int nColumns;
private int nBombs = 0;
private Cell[][] cells;
private Cell[] bombs;
private int numUnexposedRemaining;
public Board(int r, int c, int b) {
nRows = r;
nColumns = c;
nBombs = b;
initializeBoard();
shuffleBoard();
setNumberedCells();
numUnexposedRemaining = nRows * nColumns - nBombs;
}
private void initializeBoard() {
cells = new Cell[nRows][nColumns];
bombs = new Cell[nBombs];
for (int r = 0; r < nRows; r++) {
for (int c = 0; c < nColumns; c++) {
cells[r][c] = new Cell(r, c);
}
}
for (int i = 0; i < nBombs; i++) {
int r = i / nColumns;
int c = (i - r * nColumns) % nColumns;
bombs[i] = cells[r][c];
bombs[i].setBomb(true);
}
}
private void shuffleBoard() {
int nCells = nRows * nColumns;
Random random = new Random();
for (int index1 = 0; index1 < nCells; index1++) {
int index2 = index1 + random.nextInt(nCells - index1);
if (index1 != index2) {
/* Get cell at index1. */
int row1 = index1 / nColumns;
int column1 = (index1 - row1 * nColumns) % nColumns;
Cell cell1 = cells[row1][column1];
/* Get cell at index2. */
int row2 = index2 / nColumns;
int column2 = (index2 - row2 * nColumns) % nColumns;
Cell cell2 = cells[row2][column2];
/* Swap. */
cells[row1][column1] = cell2;
cell2.setRowAndColumn(row1, column1);
cells[row2][column2] = cell1;
cell1.setRowAndColumn(row2, column2);
}
}
}
private boolean inBounds(int row, int column) {
return row >= 0 && row < nRows && column >= 0 && column < nColumns;
}
/* Set the cells around the bombs to the right number. Although
* the bombs have been shuffled, the reference in the bombs array
* is still to same object. */
private void setNumberedCells() {
int[][] deltas = { // Offsets of 8 surrounding cells
{-1, -1}, {-1, 0}, {-1, 1},
{ 0, -1}, { 0, 1},
{ 1, -1}, { 1, 0}, { 1, 1}
};
for (Cell bomb : bombs) {
int row = bomb.getRow();
int col = bomb.getColumn();
for (int[] delta : deltas) {
int r = row + delta[0];
int c = col + delta[1];
if (inBounds(r, c)) {
cells[r][c].incrementNumber();
}
}
}
}
public void printBoard(boolean showUnderside) {
System.out.println();
System.out.print(" ");
for (int i = 0; i < nColumns; i++) {
System.out.print(i + " ");
}
System.out.println();
for (int i = 0; i < nColumns; i++) {
System.out.print("--");
}
System.out.println();
for (int r = 0; r < nRows; r++) {
System.out.print(r + "| ");
for (int c = 0; c < nColumns; c++) {
if (showUnderside) {
System.out.print(cells[r][c].getUndersideState());
} else {
System.out.print(cells[r][c].getSurfaceState());
}
}
System.out.println();
}
}
private boolean flipCell(Cell cell) {
if (!cell.isExposed() && !cell.isGuess()) {
cell.flip();
numUnexposedRemaining--;
return true;
}
return false;
}
public void expandBlank(Cell cell) {
int[][] deltas = {
{-1, -1}, {-1, 0}, {-1, 1},
{ 0, -1}, { 0, 1},
{ 1, -1}, { 1, 0}, { 1, 1}
};
Queue<Cell> toExplore = new LinkedList<Cell>();
toExplore.add(cell);
while (!toExplore.isEmpty()) {
Cell current = toExplore.remove();
for (int[] delta : deltas) {
int r = current.getRow() + delta[0];
int c = current.getColumn() + delta[1];
if (inBounds(r, c)) {
Cell neighbor = cells[r][c];
if (flipCell(neighbor) && neighbor.isBlank()) {
toExplore.add(neighbor);
}
}
}
}
}
public UserPlayResult playFlip(UserPlay play) {
Cell cell = getCellAtLocation(play);
if (cell == null) {
return new UserPlayResult(false, GameState.RUNNING);
}
if (play.isGuess()) {
boolean guessResult = cell.toggleGuess();
return new UserPlayResult(guessResult, GameState.RUNNING);
}
boolean result = flipCell(cell);
if (cell.isBomb()) {
return new UserPlayResult(result, GameState.LOST);
}
if (cell.isBlank()) {
expandBlank(cell);
}
if (numUnexposedRemaining == 0) {
return new UserPlayResult(result, GameState.WON);
}
return new UserPlayResult(result, GameState.RUNNING);
}
public Cell getCellAtLocation(UserPlay play) {
int row = play.getRow();
int col = play.getColumn();
if (!inBounds(row, col)) {
return null;
}
return cells[row][col];
}
public int getNumRemaining() {
return numUnexposedRemaining;
}
}